﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JXT.PrimaryKey.Batman.Collections;
using JXT.PrimaryKey.Batman.Domain.Specifications;
using JXT.PrimaryKey.Batman.Domain.Quotation.Models;
using JXT.PrimaryKey.Batman.Domain.Services;
using JXT.PrimaryKey.Batman.Domain.Quotation.Services;
using JXT.PrimaryKey.Batman.Domain.Quotation.Repositories;
using System.Threading;
using JXT.PrimaryKey.Batman.Domain.Repositories;

namespace JXT.PrimaryKey.Batman.Domain.Quotation.Services.Default
{
    public class MarketStatisticService : CachedServiceBase<MarketStatistic>, IMarketStatisticService
    {
        private static readonly int _defaultWaitTime = 20;

        public MarketStatistic GetSingle(ISpecification<MarketStatistic> specification)
        {
            var find = LoadRepository<IMarketStatisticRepository>().GetSingle(specification);
            FillSingle(find);
            return find;
        }

        public IPagedCollection<MarketStatistic> GetList(IPagedSpecification<MarketStatistic> specification)
        {
            var list = LoadRepository<IMarketStatisticRepository>().GetList(specification);
            return list;
        }

        public WeekMarketStatistic GetWeekSingle(ISpecification<WeekMarketStatistic> specification)
        {
            var find = LoadRepository<IMarketStatisticRepository>().GetWeekSingle(specification);
            return find;
        }

        public IPagedCollection<WeekMarketStatistic> GetWeekList(IPagedSpecification<WeekMarketStatistic> specification)
        {
            var list = LoadRepository<IMarketStatisticRepository>().GetWeekList(specification);
            return list;
        }

        public MonthMarketStatistic GetMonthSingle(ISpecification<MonthMarketStatistic> specification)
        {
            var find = LoadRepository<IMarketStatisticRepository>().GetMonthSingle(specification);
            return find;
        }

        public IPagedCollection<MonthMarketStatistic> GetMonthList(IPagedSpecification<MonthMarketStatistic> specification)
        {
            var list = LoadRepository<IMarketStatisticRepository>().GetMonthList(specification);
            return list;
        }

        public void Add(MarketType markettype, DateTime dt, Action<TaskExecutedStatus> updateAction = null)
        {
            if (markettype == MarketType.Unknown)
                throw new ArgumentException("参数不正确，不能markettype不能为unknow");
            if (GetMarketStatisticStatus(markettype, TimeSortType.Daily, dt))
                return;
            IEnumerable<Securities> list = LoadRepository<ISecuritiesRepository>().GetList(markettype);
            RepositoryContext.BeginTransaction();
            try
            {
                var realTimeQuoteServiceObj = LoadService<IRealTimeQuoteService>();
                int total = list.Count();
                int finished = 0;
                int skip = 0;
                double rate = 0;
                foreach (var item in list)
                {
                    //获取股票的行情数据
                    var entity = realTimeQuoteServiceObj.GetSingle(item.Code, item.MarketType);//获取股票的行情数据
                    if (null == entity || entity.UpdateTime.Date != dt.Date) //b如果取到的不是当天的行情数据，不写入
                    {
                        UpdateProcess(total, ref finished, ref skip, ref rate, true, updateAction);
                        continue;
                    }
                    var prelastprice = entity.PrevClosePrice;
                    MarketStatistic statistic = new MarketStatistic();
                    statistic.PrevClosePrice = prelastprice;//昨收价
                    statistic.MarketType = entity.MarketType;
                    statistic.OpenPrice = entity.OpenPrice;
                    statistic.ClosePrice = entity.LastPrice;
                    statistic.HighestPrice = entity.HighestPrice;
                    statistic.LowestPrice = entity.LowestPrice;
                    statistic.MarketValue = entity.LastPrice * item.CirculatingShares;
                    statistic.TradingVolume = entity.TradingVolume;
                    statistic.Turnover = entity.Turnover;
                    statistic.Code = entity.Code;
                    statistic.UpdateTime = dt.Date;
                    if (prelastprice == 0)
                    {
                        statistic.ChangePrice = 0;
                        statistic.ChangeRate = 0;
                    }
                    else
                    {
                        statistic.ChangePrice = Convert.ToDouble((statistic.ClosePrice - prelastprice).ToString("0.00"));
                        statistic.ChangeRate = Convert.ToDouble((((statistic.ClosePrice - prelastprice) / prelastprice) * 100).ToString("0.0000"));
                    }
                    LoadRepository<IMarketStatisticRepository>().Add(statistic);
                    UpdateProcess(total, ref finished, ref skip, ref rate, false, updateAction);
                    Thread.Sleep(_defaultWaitTime);
                }
                MarketStatisticStatus status = new MarketStatisticStatus()
                {
                    Markettype = markettype,
                    Status = true,
                    TimeSorttype = TimeSortType.Daily,
                    UpdateTime = dt.Date
                };
                LoadRepository<IMarketStatisticStatusRepository>().Add(status);
                RepositoryContext.CommitTransaction();
            }
            catch (Exception ex)
            {
                RepositoryContext.RollbackTransaction();
                throw ex;
            }
            finally
            {
                RepositoryContext.DisposeTransaction();
            }
        }


        public void AddWeek(MarketType markettype, DateTime dt, Action<TaskExecutedStatus> updateAction = null)
        {
            if (markettype == MarketType.Unknown)
                throw new ArgumentException("参数错误，markettype不能为unknow");
            //如果status表中，状态变为已经更新，则不在进行更新
            if (GetMarketStatisticStatus(markettype, TimeSortType.Weekly, dt))
                return;
            IEnumerable<Securities> list = LoadRepository<ISecuritiesRepository>().GetList(markettype);
            RepositoryContext.BeginTransaction();
            try
            {
                var currentWeeklyDate = GetWeekDay(dt);//当前日期所在周的开始时间和结束时间
                var updateTime = currentWeeklyDate.Item1.AddDays(5 - (int)currentWeeklyDate.Item1.DayOfWeek);//本周5,更新时间
                int total = list.Count();
                int finished = 0;
                int skip = 0;
                double rate = 0;
                foreach (var item in list)
                {
                    var currentMarketStatisticList = LoadRepository<IMarketStatisticRepository>().GetWeekList(item.Code, markettype, currentWeeklyDate);
                    var prevMarketStatistic = LoadRepository<IMarketStatisticRepository>().GetLastWeekDate(item.Code, item.MarketType, updateTime);//上周数据
                    var currentMarketStatistic = GetWeekSingle(item.Code, markettype, updateTime.Date);
                    if (currentMarketStatisticList == null || currentMarketStatisticList.Count() == 0)
                    {
                        UpdateProcess(total, ref finished, ref skip, ref rate, true, updateAction);
                        continue;
                    }
                    var firstObj = currentMarketStatisticList.FirstOrDefault();
                    var lastObj = currentMarketStatisticList.LastOrDefault();
                    WeekMarketStatistic statistic = new WeekMarketStatistic();
                    statistic.MarketType = firstObj.MarketType;
                    statistic.OpenPrice = firstObj.OpenPrice;
                    statistic.ClosePrice = lastObj.ClosePrice;
                    statistic.HighestPrice = currentMarketStatisticList.Max(e => e.HighestPrice);
                    statistic.LowestPrice = currentMarketStatisticList.Min(e => e.LowestPrice);
                    statistic.MarketValue = currentMarketStatisticList.LastOrDefault().MarketValue;
                    statistic.TradingVolume = currentMarketStatisticList.Sum(e => e.TradingVolume);
                    statistic.Turnover = currentMarketStatisticList.Sum(e => e.Turnover);
                    statistic.UpdateTime = updateTime.Date;
                    statistic.Code = item.Code;
                    statistic.TradingDays = currentMarketStatisticList.Count();
                    //计算比率
                    if (null == prevMarketStatistic)
                    {
                        statistic.ChangePrice = 0;
                        statistic.ChangeRate = 0;
                    }
                    else
                    {
                        statistic.ChangePrice = Convert.ToDouble((statistic.ClosePrice - prevMarketStatistic.ClosePrice).ToString("0.00"));
                        statistic.ChangeRate = prevMarketStatistic.ClosePrice == 0 ? 0 : Convert.ToDouble((((statistic.ClosePrice - prevMarketStatistic.ClosePrice) / prevMarketStatistic.ClosePrice) * 100).ToString("0.0000"));
                    }
                    //如果在周行情表中有当周的数据，则进行更新。如果没有，则进行添加。
                    if (statistic != null)
                    {
                        if (currentMarketStatistic == null)
                            LoadRepository<IMarketStatisticRepository>().Add(statistic);
                        else
                            LoadRepository<IMarketStatisticRepository>().Update(statistic);
                    }
                    UpdateProcess(total, ref finished, ref skip, ref rate, false, updateAction);
                    Thread.Sleep(_defaultWaitTime);
                }
                MarketStatisticStatus status = new MarketStatisticStatus()
                {
                    Markettype = markettype,
                    Status = true,
                    TimeSorttype = TimeSortType.Weekly,
                    UpdateTime = dt.Date
                };
                LoadRepository<IMarketStatisticStatusRepository>().Add(status);
                RepositoryContext.CommitTransaction();
            }
            catch (Exception ex)
            {
                RepositoryContext.RollbackTransaction();
                throw ex;
            }
            finally
            {
                RepositoryContext.DisposeTransaction();
            }
        }


        public void AddMonth(MarketType markettype, DateTime dt, Action<TaskExecutedStatus> updateAction = null)
        {
            if (markettype == MarketType.Unknown)
                throw new ArgumentException("markettype不能为unknow");
            if (GetMarketStatisticStatus(markettype, TimeSortType.Month, dt))
                return;
            IEnumerable<Securities> list = LoadRepository<ISecuritiesRepository>().GetList(markettype);
            RepositoryContext.BeginTransaction();
            try
            {
                var preparUpdateTime = dt.AddMonths(1);
                var updatetime = new DateTime(preparUpdateTime.Year, preparUpdateTime.Month, 1);//更新日期为当前时间月的下个月的1号。
                int total = list.Count();
                int finished = 0;
                int skip = 0;
                double rate = 0;
                foreach (var item in list)
                {
                    var daysInMonth = DateTime.DaysInMonth(dt.Year, dt.Month);
                    var startDate = new DateTime(dt.Year, dt.Month, 1);
                    var endDate = new DateTime(dt.Year, dt.Month, daysInMonth);
                    //从日数据中获取需要的月度数据
                    var currentMarketStatisticList = LoadRepository<IMarketStatisticRepository>().GetMonthList(item.Code, markettype, startDate.Date, endDate.Date);
                    var currentMonthMarketStatistic = LoadRepository<IMarketStatisticRepository>().GetMonthSingle(item.Code, markettype, updatetime);
                    var prevMonthMarketStatistic = LoadRepository<IMarketStatisticRepository>().GetLastMonthDate(item.Code, markettype, updatetime);
                    if (currentMarketStatisticList == null || currentMarketStatisticList.Count() == 0)
                    {
                        UpdateProcess(total, ref finished, ref skip, ref rate, true, updateAction);
                        continue;
                    }
                    var firstObj = currentMarketStatisticList.FirstOrDefault();
                    var lastObj = currentMarketStatisticList.LastOrDefault();
                    MonthMarketStatistic statistic = new MonthMarketStatistic();
                    statistic.MarketType = firstObj.MarketType;
                    statistic.OpenPrice = firstObj.OpenPrice;
                    statistic.ClosePrice = lastObj.ClosePrice;
                    statistic.HighestPrice = currentMarketStatisticList.Max(e => e.HighestPrice);
                    statistic.LowestPrice = currentMarketStatisticList.Min(e => e.LowestPrice);
                    statistic.MarketValue = lastObj.MarketValue;
                    statistic.TradingVolume = currentMarketStatisticList.Sum(e => e.TradingVolume);
                    statistic.Turnover = currentMarketStatisticList.Sum(e => e.Turnover);
                    statistic.UpdateTime = updatetime.Date;
                    statistic.Code = firstObj.Code;
                    statistic.TradingDays = currentMarketStatisticList.Count();
                    //计算比率
                    if (null == prevMonthMarketStatistic)
                    {
                        statistic.ChangePrice = 0;
                        statistic.ChangeRate = 0;
                    }
                    else
                    {
                        statistic.ChangePrice = Convert.ToDouble((statistic.ClosePrice - prevMonthMarketStatistic.ClosePrice).ToString("0.00"));
                        statistic.ChangeRate = prevMonthMarketStatistic.ClosePrice == 0 ? 0 : Convert.ToDouble((((statistic.ClosePrice - prevMonthMarketStatistic.ClosePrice) / prevMonthMarketStatistic.ClosePrice) * 100).ToString("0.0000"));
                    }
                    if (statistic != null)
                    {
                        if (currentMonthMarketStatistic == null)
                            LoadRepository<IMarketStatisticRepository>().Add(statistic);
                        else
                            LoadRepository<IMarketStatisticRepository>().Update(statistic);
                    }
                    UpdateProcess(total, ref finished, ref skip, ref rate, false, updateAction);
                    Thread.Sleep(_defaultWaitTime);
                }
                MarketStatisticStatus status = new MarketStatisticStatus()
                {
                    Markettype = markettype,
                    Status = true,
                    TimeSorttype = TimeSortType.Month,
                    UpdateTime = dt.Date
                };
                LoadRepository<IMarketStatisticStatusRepository>().Add(status);
                RepositoryContext.CommitTransaction();
            }
            catch (Exception ex)
            {
                RepositoryContext.RollbackTransaction();
                throw ex;
            }
            finally
            {
                RepositoryContext.DisposeTransaction();
            }

        }

        public IEnumerable<WeekMarketStatistic> GetWeekList(string code, MarketType markettype, Tuple<DateTime, DateTime> tuple)
        {
            return LoadRepository<IMarketStatisticRepository>().GetWeekList(code, markettype, tuple);
        }

        public WeekMarketStatistic GetWeekSingle(string code, MarketType markettype, DateTime dt)
        {
            return LoadRepository<IMarketStatisticRepository>().GetWeekSingle(code, markettype, dt);
        }


        /// <summary>
        /// 查询更新状态表的信息
        /// </summary>
        /// <param name="markettype"></param>
        /// <param name="timesort"></param>
        /// <param name="dt"></param>
        /// <returns></returns>
        private bool GetMarketStatisticStatus(MarketType markettype, TimeSortType timesort, DateTime dt)
        {
            bool flag = false;
            //获取周数据的更新时间
            var marketstatisticstatus = LoadRepository<IMarketStatisticStatusRepository>().GetMarketstatisticstatus(markettype,timesort,dt);
            if (null != marketstatisticstatus && marketstatisticstatus.Status == true)//说明已经更新，当天不需要在更新
                flag = true;
            return flag;
        }

        private void UpdateProcess(int total, ref int finished, ref int skip, ref double rate, bool isSkip, Action<TaskExecutedStatus> updateAction)
        {
            finished++;
            if (isSkip)
                skip++;
            double newRate = (double)finished / (double)total * 100;
            if ((newRate - rate >= 0.5 || finished == total) && updateAction != null)
            {
                try
                {
                    updateAction(new TaskExecutedStatus() { Finished = finished, Total = total, Skip = skip });
                }
                catch { }
                rate = newRate;
            }
        }

        #region override method
        public override RepositoryContext RepositoryContext
        {
            get
            {
                return LoadRepository<IMarketStatisticRepository>().Context;
            }
        }
        #endregion

        #region 以下需要重构成扩展方法
        /// <summary>
        /// 根据时间获取当前时间所在周的开始时间和结束时间
        /// </summary>
        /// <param name="dtime">当前时间</param>
        /// <returns>当前时间所在周的开始时间和结束时间Tuple对象</returns>
        private Tuple<DateTime, DateTime> GetWeekDay(DateTime dtime)
        {
            //确定星期几
            int index = (int)dtime.DayOfWeek;
            //当前周的范围
            DateTime retStartDay = dtime.AddDays(-index);
            DateTime retEndDay = dtime.AddDays(6 - index);
            Tuple<DateTime, DateTime> dateTimeTuple = Tuple.Create(retStartDay, retEndDay);
            return dateTimeTuple;
        }

        #endregion



        public void DeleteDayData(MarketType markettype, DateTime dt)
        {
            LoadRepository<IMarketStatisticRepository>().DeleteDayData(markettype, dt);
        }
    }
}
